Skip to content

fix(template-require-mandatory-role-attributes): use axobject-query for semantic-role exemptions#51

Merged
johanrd merged 11 commits intomasterfrom
fix/role-required-aria-checkbox-switch
Apr 25, 2026
Merged

fix(template-require-mandatory-role-attributes): use axobject-query for semantic-role exemptions#51
johanrd merged 11 commits intomasterfrom
fix/role-required-aria-checkbox-switch

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 22, 2026

[Mirror of ember-cli#2725 for Copilot review]

Note

This is part of a series where Claude has audited eslint-plugin-ember against jsx-a11y, vuejs-accessibility, angular-eslint, lit-a11y and html-validate, ember-template-lint, and the HTML and WCAG specs.

Premise

For some {element, role} pairs, the native HTML element already supplies the role's required ARIA state. Classic example: <input type="checkbox" role="switch">. The native checked state covers aria-checked, which is the required ARIA state for both role="checkbox" and role="switch" (per WAI-ARIA 1.2 §switch, switch is a subclass of checkbox whose required state is aria-checked; per HTML-AAM 1.0 §3.5.59, the native checkbox maps to the checkbox role with aria-checked derived from its checkedness). Authors shouldn't need to add aria-checked redundantly.

Problem

The rule flags any element with a role missing required ARIA attributes, including patterns where the native element already provides the state. <input type="checkbox" role="switch"> is flagged for missing aria-checked — a documented false positive.

Fix

Consult axobject-query's elementAXObjects + AXObjectRoles maps to determine when a native element satisfies a given role, mirroring eslint-plugin-jsx-a11y's isSemanticRoleElement. When the pair matches, skip the missing-attribute check for that role.

Adds axobject-query@^4.1.0 as a direct dependency. It's already installed as a transitive dep across the ecosystem; elevates to first-class.

Coverage (non-exhaustive)

Element Role Required ARIA supplied by
<input type="checkbox"> checkbox, switch native checked state
<input type="radio"> radio native checked state
<input type="range"> slider native value / min / max
<input type="number"> spinbutton native value (no required ARIA)
<input type="text"> textbox no required ARIA
<input type="search"> searchbox no required ARIA

Both angle-bracket syntax (<input type="checkbox" role="switch">) and the classic {{input type="checkbox" role="switch"}} helper are handled.

Undocumented pairings still flag

<input type="checkbox" role="menuitemcheckbox"> / <input type="radio" role="menuitemradio"> etc. — axobject-query doesn't list these, so they remain flagged for missing aria-checked. Matches jsx-a11y and angular-eslint behavior.

Why not keep the hand-list?

An earlier revision of this PR hand-maintained a 3-entry {input type}:{role} whitelist. Audit-time review showed:

  • Even the 3 entries had a factual error: the earlier 5-entry version cited axobject-query for menuitemcheckbox/menuitemradio pairings that axobject-query doesn't actually encode. A hand-list encodes the maintainer's (imperfect) understanding of the library's data; using the library directly eliminates the drift class.
  • axobject-query's coverage is strictly broader than what a hand-list would capture (see table above). A hand-list reduces false positives for 3 patterns; axobject-query reduces them for many more.
  • jsx-a11y and @angular-eslint/template already use this approach; aligning removes a documented parity gap.

Prior art

Plugin Rule Mechanism
jsx-a11y role-has-required-aria-props isSemanticRoleElement(type, attributes) via axobject-query's elementAXObjects
@angular-eslint/template role-has-required-aria Same helper, backed by axobject-query
vuejs-accessibility role-has-required-aria-props Hardcoded carve-out for {role: switch, type: checkbox} only — much narrower than axobject-query
lit-a11y role-has-required-aria-attrs No semantic-role exemption at all

This PR aligns with jsx-a11y / angular-eslint on the axobject-query-backed approach. vue-a11y's narrower carve-out and lit-a11y's no-exemption behavior are documented divergences.

What this PR does not fix

Role-token case-insensitivity at the rule level — <input type="checkbox" role="SWITCH"> is silently ignored (aria-query's roles.get(role) is case-sensitive, so the rule short-circuits to null before reaching the isSemanticRoleElement check). That rule-wide gap is addressed in ember-cli#2728.

johanrd added 2 commits April 22, 2026 00:31
…or semantic-role exemptions

Replaces the 3-entry hand-list (`{input type}:{role}` pairings) with a
lookup against axobject-query's `elementAXObjects` + `AXObjectRoles`
maps. Mirrors the approach used by eslint-plugin-jsx-a11y (its
`isSemanticRoleElement` util) and @angular-eslint/template.

## Why

The hand-list covered 3 pairings: `checkbox:checkbox`, `checkbox:switch`,
`radio:radio`. axobject-query encodes substantially more — including
`input[type=range]:slider`, `input[type=number]:spinbutton`,
`input[type=text]:textbox`, `input[type=search]:searchbox`. Each of
these is a case where the native element already provides the role's
required ARIA state (e.g., `<input type=range>` provides value via its
native `value` attribute, satisfying `role=slider`'s `aria-valuenow`
requirement).

Using axobject-query directly:
- Gives us strict superset coverage of the hand-list.
- Stays in sync when axobject-query updates (the hand-list had already
  drifted — the earlier revision incorrectly claimed menuitemcheckbox
  / menuitemradio pairings were in axobject-query when they aren't).
- Matches jsx-a11y / angular-eslint behavior, closing a documented
  parity gap.

Adds `axobject-query@^4.1.0` as a direct dep. It's already a transitive
dep via other ecosystem packages; this elevates it to first-class.

## Changes

- `lib/rules/template-require-mandatory-role-attributes.js` — replace
  `NATIVELY_CHECKED_INPUT_ROLE_PAIRS` + `isNativelyChecked` with
  `isSemanticRoleElement` that walks `elementAXObjects` and checks
  `AXObjectRoles`. Handles both `GlimmerElementNode` (angle-bracket
  syntax) and `GlimmerMustacheStatement` (classic `{{input}}` helper).
- `package.json` — add `axobject-query@^4.1.0`.
- `tests/lib/rules/template-require-mandatory-role-attributes.js` —
  add tests for the broadened coverage (`<input type=range role=slider>`
  now valid in both gts and hbs forms).
- `docs/rules/template-require-mandatory-role-attributes.md` — rewrite
  the exemption section to describe the axobject-query-backed lookup
  with a table of known pairings.
…imary source

axobject-query is the data package the rule queries, but the normative
source for "native <input type=checkbox> exposes aria-checked via the
checked IDL attribute" is HTML-AAM §el-input-checkbox. Adding the HTML-AAM
link makes the exemption's spec grounding explicit instead of leaving
axobject-query as a free-floating data source.
@johanrd johanrd requested a review from Copilot April 22, 2026 10:41
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
🟢 js small 15.33 ms 14.46 ms -5.7%
js medium 7.23 ms 7.13 ms -1.3%
🟢 js large 2.91 ms 2.82 ms -3.0%
gjs small 1.24 ms 1.24 ms -0.1%
gjs medium 613.98 µs 612.40 µs -0.3%
gjs large 245.45 µs 240.63 µs -2.0%
gts small 1.22 ms 1.21 ms -0.5%
gts medium 614.65 µs 615.97 µs +0.2%
gts large 242.96 µs 241.07 µs -0.8%

🟢 faster · 🔴 slower · 🟠 slightly slower · ⚪ within 2%

Full mitata output
clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.14.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            17.44 ms/iter  17.97 ms █ ▂                  
                      (12.83 ms … 29.66 ms)  28.21 ms █ █ ▅                
                    (  5.76 mb …  10.13 mb)   7.26 mb █▇█▇█▇▁▇▄▁▁▁▁▄▁▁▇▁▇▁▇

js small (experiment)         15.17 ms/iter  15.73 ms  █▅                  
                      (13.37 ms … 22.78 ms)  22.00 ms  ██▆▃                
                    (  6.13 mb …   8.42 mb)   6.84 mb █████▆█▄▆▄▁▁▄▁▁▁▁▁▁▁▄

                             ┌                                            ┐
                             ╷┌────────────┬┐                             ╷
          js small (control) ├┤            │├─────────────────────────────┤
                             ╵└────────────┴┘                             ╵
                               ╷┌───┬┐                  ╷
       js small (experiment)   ├┤   │├──────────────────┤
                               ╵└───┴┘                  ╵
                             └                                            ┘
                             12.83 ms           20.52 ms           28.21 ms

summary
  js small (experiment)
   1.15x faster than js small (control)

------------------------------------------- -------------------------------
js medium (control)            7.86 ms/iter   8.13 ms  █                   
                       (6.80 ms … 13.07 ms)  13.05 ms ▄█▃                  
                    (  2.45 mb …   4.58 mb)   3.53 mb ███▃▃▄▃▃▂▁▃▃▂▂▁▃▁▁▁▁▂

js medium (experiment)         7.62 ms/iter   7.74 ms  █                   
                       (6.71 ms … 13.10 ms)  12.52 ms ▂█                   
                    (  2.61 mb …   4.36 mb)   3.51 mb ████▅▂▂▂▄▁▂▃▁▁▁▁▂▁▁▁▂

                             ┌                                            ┐
                              ╷┌─────┬─┐                                  ╷
         js medium (control)  ├┤     │ ├──────────────────────────────────┤
                              ╵└─────┴─┘                                  ╵
                             ╷┌────┬┐                                 ╷
      js medium (experiment) ├┤    │├─────────────────────────────────┤
                             ╵└────┴┘                                 ╵
                             └                                            ┘
                             6.71 ms            9.88 ms            13.05 ms

summary
  js medium (experiment)
   1.03x faster than js medium (control)

------------------------------------------- -------------------------------
js large (control)             3.36 ms/iter   3.15 ms  █                   
                       (2.55 ms … 10.55 ms)   8.04 ms  █▄                  
                    (618.31 kb …   2.86 mb)   1.45 mb ▃██▂▃▃▂▁▁▁▂▁▁▂▁▁▁▁▁▁▁

js large (experiment)          3.09 ms/iter   2.93 ms  █                   
                        (2.62 ms … 7.09 ms)   5.95 ms ▃█                   
                    (321.20 kb …   2.55 mb)   1.43 mb ██▇▂▄▂▁▁▂▁▁▁▁▁▁▂▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌────┬                                     ╷
          js large (control) ├─┤    │─────────────────────────────────────┤
                             ╵ └────┴                                     ╵
                              ╷┌─┬                       ╷
       js large (experiment)  ├┤ │───────────────────────┤
                              ╵└─┴                       ╵
                             └                                            ┘
                             2.55 ms            5.29 ms             8.04 ms

summary
  js large (experiment)
   1.09x faster than js large (control)

------------------------------------------- -------------------------------
gjs small (control)            1.37 ms/iter   1.32 ms █                    
                        (1.20 ms … 6.14 ms)   5.05 ms █▂                   
                    ( 43.38 kb …   2.08 mb)   1.06 mb ██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.35 ms/iter   1.27 ms █                    
                        (1.20 ms … 5.60 ms)   4.62 ms █                    
                    (232.06 kb …   1.60 mb)   1.06 mb █▆▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gjs small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌─┬                                     ╷
      gjs small (experiment) │ │─────────────────────────────────────┤
                             └─┴                                     ╵
                             └                                            ┘
                             1.20 ms            3.12 ms             5.05 ms

summary
  gjs small (experiment)
   1.02x faster than gjs small (control)

------------------------------------------- -------------------------------
gjs medium (control)         659.41 µs/iter 626.63 µs █                    
                      (584.28 µs … 4.98 ms)   3.07 ms █                    
                    (122.94 kb …   1.10 mb) 541.34 kb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      652.93 µs/iter 626.16 µs  █                   
                      (583.26 µs … 5.26 ms)   1.57 ms ▅█                   
                    ( 11.66 kb …   1.08 mb) 540.38 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬                                           ╷
        gjs medium (control) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             ┌┬                ╷
     gjs medium (experiment) ││────────────────┤
                             └┴                ╵
                             └                                            ┘
                             583.26 µs           1.83 ms            3.07 ms

summary
  gjs medium (experiment)
   1.01x faster than gjs medium (control)

------------------------------------------- -------------------------------
gjs large (control)          272.26 µs/iter 263.37 µs  █                   
                      (234.25 µs … 4.71 ms) 393.03 µs ▅█▂                  
                    (215.75 kb … 802.39 kb) 217.11 kb ███▄█▆▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       263.32 µs/iter 258.93 µs  █                   
                      (232.57 µs … 4.51 ms) 346.12 µs ▃█▃                  
                    ( 39.02 kb … 666.82 kb) 216.40 kb ███▃▃█▆▃▂▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷ ┌────────┬                                 ╷
         gjs large (control) ├─┤        │─────────────────────────────────┤
                             ╵ └────────┴                                 ╵
                             ╷┌───────┬                      ╷
      gjs large (experiment) ├┤       │──────────────────────┤
                             ╵└───────┴                      ╵
                             └                                            ┘
                             232.57 µs         312.80 µs          393.03 µs

summary
  gjs large (experiment)
   1.03x faster than gjs large (control)

------------------------------------------- -------------------------------
gts small (control)            1.31 ms/iter   1.24 ms █                    
                        (1.19 ms … 5.92 ms)   4.98 ms █                    
                    (506.61 kb …   1.64 mb)   1.06 mb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.31 ms/iter   1.24 ms █                    
                        (1.18 ms … 6.27 ms)   5.01 ms █                    
                    (614.77 kb …   1.66 mb)   1.05 mb █▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gts small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                           ╷
      gts small (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             1.18 ms            3.10 ms             5.01 ms

summary
  gts small (experiment)
   1.01x faster than gts small (control)

------------------------------------------- -------------------------------
gts medium (control)         654.88 µs/iter 629.27 µs  █                   
                      (584.28 µs … 5.41 ms)   1.18 ms ▂█▂                  
                    (184.57 kb …   1.13 mb) 541.22 kb ███▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      661.29 µs/iter 627.69 µs █                    
                      (584.14 µs … 5.24 ms)   3.19 ms █                    
                    (245.70 kb …   1.23 mb) 540.95 kb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬        ╷
        gts medium (control) ││────────┤
                             └┴        ╵
                             ┌┬                                           ╷
     gts medium (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             584.14 µs           1.89 ms            3.19 ms

summary
  gts medium (control)
   1.01x faster than gts medium (experiment)

------------------------------------------- -------------------------------
gts large (control)          264.17 µs/iter 259.88 µs  █▃                  
                      (233.19 µs … 4.74 ms) 333.35 µs  ██                  
                    (216.09 kb … 808.86 kb) 216.88 kb ▇██▇▂▅▇▆▄▂▁▂▁▁▁▁▁▁▁▁▁

gts large (experiment)       263.18 µs/iter 258.74 µs  █                   
                      (232.00 µs … 4.75 ms) 319.88 µs  █▆                  
                    (181.27 kb … 965.80 kb) 216.71 kb ▇███▃▂▆▆▅▃▂▁▁▂▁▁▁▁▁▁▁

                             ┌                                            ┐
                              ╷┌───────────┬                              ╷
         gts large (control)  ├┤           │──────────────────────────────┤
                              ╵└───────────┴                              ╵
                             ╷ ┌───────────┬                        ╷
      gts large (experiment) ├─┤           │────────────────────────┤
                             ╵ └───────────┴                        ╵
                             └                                            ┘
                             232.00 µs         282.68 µs          333.35 µs

summary
  gts large (experiment)
   1x faster than gts large (control)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates template-require-mandatory-role-attributes to avoid false positives when a native HTML element already supplies a role’s required ARIA state, by consulting axobject-query for semantic element↔role pairings.

Changes:

  • Add an axobject-query-backed semantic-role exemption (mirroring jsx-a11y / angular-eslint behavior).
  • Expand rule tests to cover exempt vs non-exempt <input type=... role=...> pairings (including {{input}} helper cases).
  • Document the semantic-role exemption behavior and add axobject-query as a direct dependency.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/rules/template-require-mandatory-role-attributes.js Adds axobject-query lookup to skip required-ARIA enforcement when the node is a semantic role element.
tests/lib/rules/template-require-mandatory-role-attributes.js Adds valid/invalid cases for semantic input-role exemptions plus non-exempt pairings.
tests/audit/role-has-required-aria/peer-parity.js Introduces an audit-only parity fixture (not in CI) documenting peer behaviors.
package.json Adds axobject-query@^4.1.0 as a direct dependency.
docs/rules/template-require-mandatory-role-attributes.md Documents semantic-role exemptions and adds references.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/rules/template-require-mandatory-role-attributes.md
Comment thread tests/audit/role-has-required-aria/peer-parity.js Outdated
Comment thread lib/rules/template-require-mandatory-role-attributes.js Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/rules/template-require-mandatory-role-attributes.js
Comment thread tests/lib/rules/template-require-mandatory-role-attributes.js
Comment thread tests/audit/role-has-required-aria/peer-parity.js Outdated
Comment thread docs/rules/template-require-mandatory-role-attributes.md Outdated
@johanrd johanrd force-pushed the fix/role-required-aria-checkbox-switch branch from f735a42 to 48ef43c Compare April 22, 2026 17:11
johanrd added 4 commits April 23, 2026 21:39
…e {{input}} assumption (Copilot review)

In strict GJS/GTS, {{input}} could resolve to any imported identifier — not
just Ember's classic helper. We assume the classic helper (renders native
input); false-positive risk is low in practice because strict-mode authors
rarely use the mustache form at all. Document the assumption inline so
future readers don't need to re-derive the tradeoff.
…Objects by tag (Copilot review)

Benchmarked ~12.5x speedup on isSemanticRoleElement. The naive impl walked
the full elementAXObjects map per call (O(concepts × axObjects × roles));
pre-indexing resolves each concept's exposed-role set once at module load
and buckets concepts by tag, reducing the per-call hot path to a handful
of entries per tag.

Benchmark: 200k calls on a realistic tag/role mix — current 154 ms, indexed
12 ms. Behavior-preserving (140/140 parity combos verified before landing;
84/84 rule-test suite passes).
johanrd added a commit that referenced this pull request Apr 24, 2026
…havior

Rebased onto #51's axobject-query semantic-role-element exemption. The
audit fixture previously captured pre-#54 divergences (our rule accepted
space-separated and case-insensitive role values) as VALID-for-us. After
rebase, the rule splits whitespace + lowercases before lookup, so
`<div role="combobox listbox">` and `<div role="COMBOBOX">` now flag —
matching jsx-a11y. Move those cases from valid → invalid and rewrite the
divergence comments as parity.
@johanrd johanrd requested a review from Copilot April 24, 2026 08:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/rules/template-require-mandatory-role-attributes.js
Comment thread tests/audit/role-has-required-aria/peer-parity.js Outdated
johanrd added a commit that referenced this pull request Apr 24, 2026
… by tag (Copilot review)

Benchmarked ~2.6× speedup on getImplicitRole — bucketing the static
elementRoles Map by tag turns the per-call scan of ~80 keys into a 1-5
key lookup per tag. Parity verified across 15 representative tag/attr
combinations before landing. Matches the Q29 optimization pattern on
#51's isSemanticRoleElement.
@johanrd johanrd requested a review from Copilot April 24, 2026 13:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

johanrd added 2 commits April 25, 2026 06:33
…e cases, drop audit fixture

Upstream maintainers don't want the per-PR `tests/audit/peer-parity`
pattern. Port the documented divergences from peer plugins (jsx-a11y,
vue-a11y, angular-eslint) into the regular suite as VALID cases that
pin our current behavior:
- `role="combobox listbox"` (space-separated tokens — peers split,
  we don't)
- `role="COMBOBOX"` / `role="SLIDER"` (case-insensitivity — peers
  lowercase before lookup, we don't)
- `role="foobar"` (unknown — parity)

Other audit cases were already covered by the regular tests or were
pure peer-plugin restatements.
- Replace broken `html-aam-1.1` URL with `html-aam-1.0`. There is no
  HTML-AAM 1.1 published recommendation; 1.0 is the published TR and
  contains the cited `#el-input-checkbox` anchor.
- Soften the description of how the native element supplies aria-checked.
  HTML-AAM 1.0 §3.5.59 derives the value from the element's checkedness
  (plus `indeterminate` for the `mixed` value), not directly from the
  `checked` IDL attribute.
@johanrd johanrd merged commit 57cdaf9 into master Apr 25, 2026
10 checks passed
johanrd added a commit that referenced this pull request Apr 25, 2026
… by tag (Copilot review)

Benchmarked ~2.6× speedup on getImplicitRole — bucketing the static
elementRoles Map by tag turns the per-call scan of ~80 keys into a 1-5
key lookup per tag. Parity verified across 15 representative tag/attr
combinations before landing. Matches the Q29 optimization pattern on
#51's isSemanticRoleElement.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants